#include "NBody.h"
#include <time.h>
#include <vector>
#include <ctime>
#define STRINGIFY(src) #src

static const std::string kernelString =
		#include "NBodyKernel.c"
	"";

NBody::NBody(cl_float5* points_, int size_)
{
	//device = NULL;
	points = points_;
	size = size_;
}
NBody::NBody(cl::Device device_, cl_float5* points_, int size_)
{
	this->device = device_;
	
	this->context = cl::Context(device);

	this->queue = cl::CommandQueue(context, device);

	cl::Program::Sources sources;

	sources.push_back(std::pair<const char*, size_t>(kernelString.c_str(), kernelString.length()));
	cl::Program program(context, sources);

	if (program.build() != CL_SUCCESS)
	{
		std::cout << " Error building: " << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device) << "\n";
		exit(-1);
	}

	this->kernel_nBody = cl::Kernel(program, "NBody");
	this->change(points_, size_);


}

void NBody::change(cl_float5* points_, int size_)
{
	this->points = points_;
	this->size = size_;
	this->buffer_points = cl::Buffer(this->context, CL_MEM_READ_WRITE, sizeof(cl_float5)* this->size);
	this->buffer_n = cl::Buffer(this->context, CL_MEM_READ_ONLY, sizeof(int)* 2);
	this->kernel_nBody.setArg(0, buffer_points);
	this->kernel_nBody.setArg(1, buffer_n);
}

NBody::~NBody()
{
}

double diffclock(clock_t clock1, clock_t clock2)
{
	double diffticks = clock1 - clock2;
	double diffms = (diffticks) / (CLOCKS_PER_SEC / 1000);
	return diffms;
}
bool NBody::startWork(int iterNow, int iterMax)
{
	//int workGroupSize = (int)device.getInfo<CL_DEVICE_MAX_WORK_GROUP_SIZE>();

	pieces = size / iterMax;
	offset = pieces*iterNow;

	if (iterNow == iterMax - 1)
		pieces = size - (pieces*(iterMax - 1));

	//std::cout << offset << " to " << offset+pieces << std::endl;

	queue.enqueueWriteBuffer(buffer_points, CL_FALSE, 0, sizeof(cl_float5) * size, points);
	//n = new int[]{ size, offset};
	n    = new int[2];
	n[0] = size;
	n[1] = offset;
	queue.enqueueWriteBuffer(buffer_n, CL_FALSE, 0, sizeof(int) * 2, n);
	queue.flush();
	//int worksize = device.getInfo<CL_DEVICE_MAX_WORK_GROUP_SIZE>();

	//int units = (pieces / (float) worksize + 0.999999999999999999);
	//std::cout << units << std::endl;
	
		queue.enqueueNDRangeKernel(
			kernel_nBody,
			cl::NullRange, // offset starts at 0,0
			cl::NDRange(size), // device.getInfo<CL_DEVICE_MAX_COMPUTE_UNITS>(), // number of work groups
			cl::NullRange, //device.getInfo<CL_DEVICE_MAX_WORK_GROUP_SIZE>(), // workgroup size
			NULL, &event);
		//queue.finish();
/*

	queue.enqueueNDRangeKernel(kernel_nBody, cl::NullRange, cl::NDRange(size),));
	//queue.flush();
*/
	//queue.finish();
	//queue.enqueueReadBuffer(buffer_points, CL_TRUE, 0, sizeof(cl_float5) * size, points);
	
	return true;
}

void NBody::finish()
{
	event.wait();
	event = NULL;
	//queue.finish();
	queue.enqueueReadBuffer(buffer_points, CL_TRUE, sizeof(cl_float5) * (offset), sizeof(cl_float5) * (pieces), points);
	queue.finish();
}

NBody* NBody::copy()
{
	NBody* temp = new NBody(device, points, size );
	return temp;
}
NBody* NBody::copy(cl::Device device_)
{
	std::cout << (int) &device << " == " << (int) &device_ << std::endl;
	NBody* temp = new NBody(device_, points, size );
	return temp;
}